package com.changpei.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.changpei.common.cache.UserCache;
import com.changpei.common.exception.custom.CustomException;
import com.changpei.common.http.HttpClient;
import com.changpei.common.result.enums.CommonCode;
import com.changpei.common.utils.JsonUtil;
import com.changpei.core.config.OauthConfig;
import com.changpei.core.config.RabbitInfoConfig;
import com.changpei.core.pojo.User;
import com.changpei.core.service.OauthService;
import com.changpei.core.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Way on 2021/8/6
 */
@Service
@Slf4j
public class OauthServiceImpl implements OauthService {

    @Autowired
    private OauthConfig oauthConfig;

    @Autowired
    private UserService userService;

    @Autowired
    private UserCache userCache;

    @Autowired
    private RabbitInfoConfig rabbitInfoConfig;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 锁对象
     */
    private final Lock lock = new ReentrantLock();

    @Override
    public JSONObject getToken(String code) throws Exception {
        String url = String.format(oauthConfig.getLoginUrl(), oauthConfig.getAppId(), oauthConfig.getAppSecret(), code);

        // 发送http请求
        HttpClient httpClient = new HttpClient(url);
        httpClient.get();

        // 响应内容
        String content = httpClient.getContent();
        if (ObjectUtils.isEmpty(content)) {
            throw new CustomException(CommonCode.WECHAT_LOGIN_TOKEN);
        } else {
            JSONObject wxResult = JSONObject.parseObject(content);
            String errKey = "errcode";
            if (wxResult.containsKey(errKey)) {
                CommonCode commonCode = CommonCode.WECHAT_LOGIN_TOKEN;
                commonCode.setMessage(wxResult.getString("errmsg"));
                throw new CustomException(commonCode);
            } else {
                return grantToken(wxResult);
            }
        }
    }

    /**
     * 颁发令牌
     * 1.拿到openid
     * 2.去数据库查询这个openid是否存在，如果存在则不做处理，如果不存在，则新增一条记录
     * 3.生成令牌，准备缓存数据(将用户id缓存起来)，写入缓存
     * 4.返回令牌给客户端
     *
     * @param jsonObject
     * @return
     */
    private JSONObject grantToken(JSONObject wxResult) throws CustomException {

        JSONObject r = new JSONObject();

        String openid = wxResult.getString("openid");
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("openid", openid);
        User data = userService.getOne(queryWrapper);
        if (ObjectUtils.isEmpty(data)) {
            User newUser = new User();
            newUser.setOpenid(openid);
            userService.save(newUser);
            wxResult.put("userInfo", prepareUserInfo(newUser));
        } else {
            wxResult.put("userInfo", prepareUserInfo(data));
        }

        // 生成令牌并且写入缓存
        String token = RandomStringUtils.randomAlphanumeric(32);

        Boolean result = userCache.write(token, wxResult, oauthConfig.getTokenExpireIn());
        if (!result) {
            throw new CustomException(CommonCode.SERVER_CACHE_ERROR);
        }

        r.put("token", token);
        // 未更新资料 -> 默认未授权
        r.put("oauth", userCache.oauthStatus(wxResult.getJSONObject("userInfo").getLong("id")));

        return r;
    }

    @Override
    public boolean verify(String token) {
        return userCache.valid(token);
    }

    @Override
    public Boolean updateUserInfo(Map<String, Object> params) {

        // 加锁
        lock.lock();
        try {
            String token = params.get("token").toString();
            Object user = params.get("user");

            Long userId = userCache.getUserId(token);
            JSONObject userInfo = JSONObject.parseObject(JSON.toJSONString(user));
            userInfo.put("id", userId);

            // 获取源数据
            JSONObject cacheData = userCache.original(token);
            cacheData.put("userInfo", userInfo);

            // 更新mysql数据库
            User bean = JsonUtil.jsonToBean(userInfo, User.class);
            userService.updateById(bean);

            // 写入缓存
            userCache.write(token, cacheData, oauthConfig.getTokenExpireIn());

            // 发送到rabbitMQ,通知es修改信息
            rabbitTemplate.convertAndSend(rabbitInfoConfig.getExchange().get("userModify"),
                    rabbitInfoConfig.getRouting().get("userModify"),
                    JSON.toJSONString(userInfo, SerializerFeature.WriteMapNullValue,
                            SerializerFeature.WriteNullNumberAsZero));

            // 更新授权状态
            userCache.oauth(userId);

            return true;
        } catch (Exception e) {
            log.error("更新用户信息失败", e.getMessage());
            return false;
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    /**
     * 准备用户缓存数据
     *
     * @param user
     * @return
     */
    private JSONObject prepareUserInfo(User user) {
        JSONObject userInfo = new JSONObject();
        userInfo.put("id", user.getId());
        userInfo.put("nickName", user.getNickName());
        userInfo.put("gender", user.getGender());
        userInfo.put("avatarUrl", user.getAvatarUrl());
        userInfo.put("city", user.getCity());
        userInfo.put("province", user.getProvince());
        userInfo.put("country", user.getCountry());
        return userInfo;
    }

}
